package com.flyqiu.common.lock;

import org.slf4j.Logger;

import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Supplier;

/**
 * FlyQiuLockTools 类提供了一种基于锁的机制，用于在并发环境下控制对共享资源的访问
 */
public class FlyQiuLockTools {
    // 使用 ConcurrentHashMap 来存储不同锁键对应的锁对象，确保线程安全
    private static final ConcurrentHashMap<String, LockHolder> locks = new ConcurrentHashMap<>();
    // 日志记录器，用于记录锁的获取和释放信息
    private static final Logger logger = org.slf4j.LoggerFactory.getLogger(FlyQiuLockTools.class);

    /**
     * 获取锁对象，并增加其引用计数
     *
     * @param lockKey 锁的唯一键，通常基于共享资源的标识生成
     * @return 锁对象，供后续的同步控制使用
     */
    private static LockHolder getLock(String lockKey) {
        // 如果锁不存在，则创建新的锁对象；存在则返回现有锁对象，并将引用计数设置为1
        LockHolder lockHolder = locks.computeIfAbsent(lockKey, k -> new LockHolder());
        lockHolder.getRefNum().incrementAndGet(); // 将 refNum 设置为 1
        logger.info("Lock acquired for key: {}", lockKey);
        return lockHolder;
    }

    /**
     * 执行给定的 Runnable 任务，使用指定的锁键进行同步控制
     *
     * @param lockKey 锁的唯一键
     * @param runnable 需要在锁保护下执行的任务
     */
    private static void exec(String lockKey, Runnable runnable) {
        // 获取锁对象，并在 synchronized 块中执行任务，确保线程安全
        LockHolder lock = getLock(lockKey);
        synchronized (lock) {
            try {
                runnable.run();
            } catch (Exception e) {
                // 记录任务执行过程中的异常，并重新抛出
                logger.error("Error executing runnable for key: {}, error: {}", lockKey, e.getMessage(), e);
                throw e;
            } finally {
                // 任务完成后，减少锁的引用计数，并可能移除锁
                lock.getRefNum().decrementAndGet();
                if (lock.getRefNum().get() < 1) {
                    locks.remove(lockKey);
                    logger.info("Lock released for key: {}", lockKey);
                }
            }
        }
    }

    /**
     * 执行给定的 Supplier 任务，使用指定的锁键进行同步控制
     *
     * @param lockKey 锁的唯一键
     * @param run 需要在锁保护下执行并返回结果的任务
     * @param <T> 任务返回值的类型
     * @return 任务的执行结果
     */
    public static <T> T exec(String lockKey, Supplier<T> run) {
        // 获取锁对象，并在 synchronized 块中执行任务，确保线程安全
        LockHolder lock = getLock(lockKey);
        synchronized (lock) {
            try {
                return run.get();
            } catch (Exception e) {
                // 记录任务执行过程中的异常，并重新抛出
                logger.error("Error executing runnable for key: {}, error: {}", lockKey, e.getMessage(), e);
                throw e;
            } finally {
                // 任务完成后，减少锁的引用计数，并可能移除锁
                lock.getRefNum().decrementAndGet();
                if (lock.getRefNum().get() < 1) {
                    locks.remove(lockKey);
                    logger.info("Lock released for key: {}", lockKey);
                }
            }
        }
    }

}
